package cn.sccl.common.scheduler;

import cn.sccl.common.exception.BusinessException;
import cn.sccl.common.scheduler.enums.JobStateEnum;
import cn.sccl.common.scheduler.enums.JobTypeEnum;
import cn.sccl.common.scheduler.job.BaseJob;
import cn.sccl.common.scheduler.model.TaskScheduled;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.*;
import org.quartz.Trigger.TriggerState;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.InitializingBean;

import java.sql.Timestamp;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * 默认的定时任务管理器
 *
 * @author _minus
 * @create 2017-10-04 14:18
 */
public class SchedulerManager implements InitializingBean {

    private Log logger = LogFactory.getLog(this.getClass());

    private Scheduler scheduler;

    private List<JobListener> jobListeners;

    public void setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    public void setJobListeners(List<JobListener> jobListeners) {
        this.jobListeners = jobListeners;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (this.jobListeners != null && this.jobListeners.size() > 0) {
            logger.info("Initing task scheduler[" + this.scheduler.getSchedulerName() + "] , add listener size ："
                    + this.jobListeners.size());
            for (JobListener listener : this.jobListeners) {
                logger.info("Add JobListener : " + listener.getName());
                this.scheduler.getListenerManager().addJobListener(listener);
            }
        }
    }


    /**
     * 获取所有任务
     *
     * @return
     */
    public List<TaskScheduled> getAllJobDetail() {
        List<TaskScheduled> result = new LinkedList<TaskScheduled>();
        try {
            GroupMatcher<JobKey> matcher = GroupMatcher.jobGroupContains("");
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            for (JobKey jobKey : jobKeys) {
                List<TaskScheduled> tsList = getTaskScheduledListByJobKey(jobKey);
                result.addAll(tsList);
            }
        } catch (Exception e) {
            logger.error("Try to load All JobDetail cause error : ", e);
        }
        return result;
    }

    /**
     * 一个job 可以有多个trigger,这里 我们将相同的job，逻辑上作为不同的job（即一个trigger 一个job）
     * @param jobKey
     * @return
     * @throws SchedulerException
     */
    public List<TaskScheduled> getTaskScheduledListByJobKey(JobKey jobKey) throws SchedulerException {
        List<TaskScheduled> result = new LinkedList<TaskScheduled>();
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        for (Trigger trigger : triggers) {
            TaskScheduled job = new TaskScheduled();
            job.setTaskName(jobKey.getName());
            job.setTaskGroup(jobKey.getGroup());
            TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            job.setStatus(JobStateEnum.getJobStateEnumByType(triggerState.name()).getName());
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger)trigger;
                String cronExpression = cronTrigger.getCronExpression();
                job.setTaskCron(cronExpression);
            }
            Date perTime = trigger.getPreviousFireTime() ;
            Date nextTime = trigger.getNextFireTime() ;
            if (perTime!=null){
                job.setPreviousFireTime(new Timestamp(perTime.getTime()));
            }
            if (nextTime!=null){
                job.setNextFireTime(new Timestamp(nextTime.getTime()));
            }
            JobDataMap jobDataMap = trigger.getJobDataMap();
            job.setTaskType(jobDataMap.getString("taskType"));
            job.setTargetSystem(jobDataMap.getString("targetSystem"));
            job.setTargetObject(jobDataMap.getString("targetObject"));
            job.setTargetMethod(jobDataMap.getString("targetMethod"));

            job.setTargetParams(JSONObject.toJSON(jobDataMap.get("targetParams")));

            job.setStartTime((Timestamp) jobDataMap.get("startTime"));
            job.setEndTime( (Timestamp) jobDataMap.get("endTime"));


            job.setTaskDesc(jobDetail.getDescription());
            String jobClass = jobDetail.getJobClass().getSimpleName();
            if (jobClass.equals(JobTypeEnum.STATEFUL_JOB.getType())) {
                job.setJobType("statefulJob");
            } else if (jobClass.equals("DefaultJob")) {
                job.setJobType("job");
            }
            result.add(job);
        }

        return result ;
    }

    public JobDetail getJobDetailByTriggerName(Trigger trigger) {
        try {
            return this.scheduler.getJobDetail(trigger.getJobKey());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 新增or 修改job
     * 
     * @param taskScheduled
     *
     * @throws Exception
     */
    public boolean updateTask(TaskScheduled taskScheduled) {
    	String jobGroup = taskScheduled.getTaskGroup();
        if (StringUtils.isEmpty(jobGroup)) {
			jobGroup = "default_job";
		}
        String jobName = taskScheduled.getTaskName();
        if (StringUtils.isEmpty(jobName)) {
            jobName = String.valueOf(System.currentTimeMillis());
        }
        String cronExpression = taskScheduled.getTaskCron();
        String targetObject = taskScheduled.getTargetObject();
        String targetMethod = taskScheduled.getTargetMethod();
        Object targetParams = taskScheduled.getTargetParams();
        String jobDescription = taskScheduled.getTaskDesc();
        String jobType = taskScheduled.getJobType();
        String taskType = taskScheduled.getTaskType();
        Timestamp startTime = taskScheduled.getStartTime() ;
        Timestamp endTime = taskScheduled.getEndTime() ;
        Long exeCnt = taskScheduled.getExeCnt() ;

        JobDataMap jobDataMap = new JobDataMap();
        //如果是执行其他服务的任务 在这设置 targetSystem

        jobDataMap.put("targetObject", targetObject);
        jobDataMap.put("targetMethod", targetMethod);
        jobDataMap.put("taskType", taskType);
        jobDataMap.put("group", jobGroup);
        jobDataMap.put("taskName", jobName);

        jobDataMap.put("startTime",startTime);
        jobDataMap.put("endTime",endTime);

        //所执行方法的参数
        if (targetParams == null){
            jobDataMap.put("targetParams", "{}");
        }else{
            jobDataMap.put("targetParams", JSON.toJSONString(targetParams));
        }


        JobBuilder jobBuilder = null;
        if (JobTypeEnum.JOB.getType().equals(jobType)) {
            jobBuilder = JobBuilder.newJob(BaseJob.class);
        } else if (JobTypeEnum.STATEFUL_JOB.getType().equals(jobType)) {
            //阻塞任务调用
            jobBuilder = JobBuilder.newJob(cn.sccl.common.scheduler.StatefulJob.class);
        }
        if (jobBuilder != null) {
            JobDetail jobDetail = jobBuilder.withIdentity(jobName, jobGroup).withDescription(jobDescription)
                .storeDurably(true).usingJobData(jobDataMap).build();


            TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                    .withIdentity(jobName, jobGroup).withDescription(jobDescription).forJob(jobDetail)
                    .usingJobData(jobDataMap) ;


            if (startTime!=null){
                //设置任务开始时间
                triggerBuilder = triggerBuilder.startAt(startTime);
            }
            if (endTime!=null){
                //设置任务结束时间
                triggerBuilder = triggerBuilder.endAt(endTime) ;
            }


            Trigger trigger = triggerBuilder.build();

            try {
                JobDetail detail = scheduler.getJobDetail(new JobKey(jobName, jobGroup));
                if (detail == null) {
                    //新增的task
                    scheduler.scheduleJob(jobDetail, trigger);
                } else {
                    //修改以前的task
                    scheduler.addJob(jobDetail, true);
                    scheduler.rescheduleJob(new TriggerKey(jobName, jobGroup), trigger);
                }
                return true;
            } catch (SchedulerException e) {
                logger.error("SchedulerException");
                throw new BusinessException(e);
            }
        }
        return false;
    }

    /**
     * 暂停所有触发器
     * 
     * @return
     */
    public void pauseAllTrigger() {
        try {
            scheduler.standby();
        } catch (SchedulerException e) {
            logger.error("SchedulerException");
            throw new BusinessException(e);
        }
    }

    /**
     * 启动所有触发器
     * 
     * @return
     */
    public void startAllTrigger() {
        try {
            if (scheduler.isInStandbyMode()) {
                scheduler.start();
            }
        } catch (SchedulerException e) {
            logger.error("SchedulerException");
            throw new BusinessException(e);
        }
    }

    // 暂停任务
    public void stopJob(TaskScheduled scheduleJob) {
        try {
            JobKey jobKey = JobKey.jobKey(scheduleJob.getTaskName(), scheduleJob.getTaskGroup());
            scheduler.pauseJob(jobKey);
        } catch (Exception e) {
            logger.error("Try to stop Job cause error : ", e);
            throw new BusinessException(e);
        }
    }

    public void stopJobByGroup(TaskScheduled scheduled) throws SchedulerException {
        if (StringUtils.isEmpty(scheduled.getTaskGroup())){
            GroupMatcher<JobKey> matcher = GroupMatcher.groupEquals(scheduled.getTaskGroup());
            scheduler.pauseJobs(matcher);
        }
    }

    // 启动任务
    public void resumeJob(TaskScheduled scheduleJob) {
        try {
            JobKey jobKey = JobKey.jobKey(scheduleJob.getTaskName(), scheduleJob.getTaskGroup());
            scheduler.resumeJob(jobKey);
        } catch (Exception e) {
            logger.error("Try to resume Job cause error : ", e);
            throw new BusinessException(e);
        }
    }

    // 执行任务
    public void runJob(TaskScheduled scheduleJob) {
        try {
            JobKey jobKey = JobKey.jobKey(scheduleJob.getTaskName(), scheduleJob.getTaskGroup());
            scheduler.triggerJob(jobKey);
        } catch (Exception e) {
            logger.error("Try to resume Job cause error : ", e);
            throw new BusinessException(e);
        }
    }

    // 删除任务
    public void delJob(TaskScheduled scheduleJob) {
        try {
            JobKey jobKey = JobKey.jobKey(scheduleJob.getTaskName(), scheduleJob.getTaskGroup());
            TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getTaskName(), scheduleJob.getTaskGroup());
            scheduler.pauseTrigger(triggerKey);// 停止触发器
            scheduler.unscheduleJob(triggerKey);// 移除触发器
            scheduler.deleteJob(jobKey);// 删除任务
        } catch (Exception e) {
            logger.error("Try to resume Job cause error : ", e);
            throw new BusinessException(e);
        }
    }


    /**
     *  判断任务是否已经存在,在新建一个任务的时候，需要保证task_name task_group 不重复
     * @param scheduleJob
     * @return
     */
    public boolean isExist(TaskScheduled scheduleJob){

        JobDetail detail = null ;
        try {
            detail = scheduler.getJobDetail(new JobKey(scheduleJob.getTaskName(), scheduleJob.getTaskGroup()));
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return detail ==null ? false :true ;
    }

    public TaskScheduled getJobDetail(TaskScheduled scheduleJob) {
        TaskScheduled taskScheduled = new TaskScheduled();
        JobKey jobKey = new JobKey(scheduleJob.getTaskName(), scheduleJob.getTaskGroup());
        try {
            List<TaskScheduled>  ts = getTaskScheduledListByJobKey(jobKey);
            if (ts!=null && !ts.isEmpty()){
                taskScheduled = ts.get(0);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return taskScheduled ;
    }
}
